include "math.h";

rules MyRewriteRules {
    ("a specific string") => printf("%s\n", "got replaced");
    (5) => 6;
    (${x: Int} %% 1) => $x + 1;
    (apple) => true;
    (banana) => strawberry;
}

function a(): Double {
    printf("side effect a\n");
    return 1;
}

function b(): Double {
    printf("side effect b\n");
    return 2;
}

rules Reduce {
    (pow($x + $y, 2)) => (pow($x, 2) + 2 * $x * $y + pow($y, 2));
}

struct MyStruct {
    var myField: Int;

    rules {
        ($this.property) => $this.myField;
    }
}

trait MyTrait {
    public function doSomething(): Void;

    rules {
        ($this.propertyWithSideEffects) => $this.doSomething();
        (for i in $this {$e}) => {
            printf("%s\n", "tried to iterate over MyTrait");
            $this.doSomething();
            $e;
        }
    }
}

implement MyTrait for MyStruct {
    public function doSomething() {
        printf("%s\n", "this is MyStruct's MyTrait implementation");
    }
}

function main() {
    printf("%i\n", 5_i32);

    using rules MyRewriteRules {
        var a = "a specific string";
        var b = "some other string";

        // custom operator; must be rewritten or will fail to compile
        var x: Int = 5;
        // var x = 5_i32;
        printf("%i\n", x);
        printf("%i\n", x %% 1);

        // var doesn't exist; must be rewritten or will fail to compile
        if (apple) {
            printf("apple => true\n");
        }

        var strawberry = true;
        if (banana) {
            printf("banana => strawberry\n");
        }
    }

    using rules Reduce {
        var a: Double = pow(a() + b(), 2);
        printf("%.2f\n", a);
    }

    var s = struct MyStruct {myField: 5};
    printf("%i\n", s.myField);
    printf("%i\n", s.property);

    var box: Box[MyTrait] = s;
    box.propertyWithSideEffects;
    for i in box {
        printf("hello\n");
    }
}
